home *** CD-ROM | disk | FTP | other *** search
- /* substitutions.c -- The part of the shell that does parameter,
- command, and globbing substitutions. */
-
- /* Copyright (C) 1987,1989 Free Software Foundation, Inc.
-
- This file is part of GNU Bash, the Bourne Again SHell.
-
- Bash is free software; you can redistribute it and/or modify it under
- the terms of the GNU General Public License as published by the Free
- Software Foundation; either version 1, or (at your option) any later
- version.
-
- Bash is distributed in the hope that it will be useful, but WITHOUT ANY
- WARRANTY; without even the implied warranty of MERCHANTABILITY or
- FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
- for more details.
-
- You should have received a copy of the GNU General Public License along
- with Bash; see the file COPYING. If not, write to the Free Software
- Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
-
- #include <stdio.h>
- #include <sys/types.h>
- #include <pwd.h>
- #include <signal.h>
- #include <string.h>
- #include "shell.h"
- #include "flags.h"
- #include "alias.h"
- #include "jobs.h"
- #include "filecntl.h"
- #include <readline/history.h>
- #include <glob/fnmatch.h>
-
- /* The size that strings change by. */
- #define DEFAULT_ARRAY_SIZE 512
-
- /* How to quote and dequote the character C. */
- #define QUOTE_CHAR(c) ((unsigned char)(c) | 0x80)
- #define DEQUOTE_CHAR(c) ((unsigned char)(c) & 0x7f)
- #define QUOTED_CHAR(c) ((unsigned char)(c) > 0x7f)
-
- /* Process ID of the last command executed within command substitution. */
- pid_t last_command_subst_pid = NO_PID;
-
- /* Some forward declarations. */
-
- extern WORD_LIST *expand_string (), *expand_word (), *list_string ();
- extern char *string_list ();
- extern WORD_DESC *make_word ();
- extern WORD_DESC *copy_word ();
- extern WORD_LIST *copy_word_list();
-
- static WORD_LIST *expand_string_internal (), *expand_words_internal ();
- static char *quote_string (), *dequote_string ();
- static int unquoted_substring ();
- static void quote_list ();
-
- /* **************************************************************** */
- /* */
- /* Utility Functions */
- /* */
- /* **************************************************************** */
-
- /* Cons a new string from STRING starting at START and ending at END,
- not including END. */
- char *
- substring (string, start, end)
- char *string;
- int start, end;
- {
- register int len = end - start;
- register char *result = (char *)xmalloc (len + 1);
-
- strncpy (result, string + start, len);
- result[len] = '\0';
- return (result);
- }
-
- /* Just like string_extract, but doesn't hack backslashes or any of
- that other stuff. */
- char *
- string_extract_verbatim (string, sindex, charlist)
- char *string, *charlist;
- int *sindex;
- {
- register int i = *sindex;
- int c;
- char *temp;
-
- while ((c = string[i]) && (!member (c, charlist))) i++;
- temp = (char *)xmalloc (1 + (i - *sindex));
- strncpy (temp, string + (*sindex), i - (*sindex));
- temp[i - (*sindex)] = '\0';
- *sindex = i;
- return (temp);
- }
-
- /* Extract a substring from STRING, starting at SINDEX and ending with
- one of the characters in CHARLIST. Don't make the ending character
- part of the string. Leave SINDEX pointing at the ending character.
- Understand about backslashes in the string. */
- char *
- string_extract (string, sindex, charlist)
- char *string, *charlist;
- int *sindex;
- {
- register int c, i = *sindex;
- char *temp;
-
- while (c = string[i]) {
- if (c == '\\')
- if (string[i + 1])
- i++;
- else
- break;
- else
- if (member (c, charlist))
- break;
- i++;
- }
- temp = (char *)xmalloc (1 + (i - *sindex));
- strncpy (temp, string + (*sindex), i - (*sindex));
- temp[i - (*sindex)] = '\0';
- *sindex = i;
- return (temp);
- }
-
- /* Remove backslashes which are quoting backquotes from STRING. Modifies
- STRING, and returns a pointer to it. */
- char *
- de_backslash (string)
- char *string;
- {
- register int i, l = strlen (string);
-
- for (i = 0; i < l; i++)
- if (string[i] == '\\' && (string[i + 1] == '`' || string[i + 1] == '\\' ||
- string[i + 1] == '$'))
- strcpy (&string[i], &string[i + 1]);
- return (string);
- }
-
- /* Replace instances of \! in a string with !. */
- void
- unquote_bang (string)
- char *string;
- {
- register int i, j;
- register char *temp;
-
- temp = (char *)alloca (1 + strlen (string));
-
- for (i = 0, j = 0; (temp[j] = string[i]); i++, j++)
- {
- if (string[i] == '\\' && string[i + 1] == '!')
- {
- temp[j] = '!';
- i++;
- }
- }
- strcpy (string, temp);
- }
-
- /* Extract the $( construct in STRING, and return a new string.
- Start extracting at (SINDEX) as if we had just seen "$(".
- Make (SINDEX) get the position just after the matching ")". */
- char *
- extract_command_subst (string, sindex)
- char *string;
- int *sindex;
- {
- char *extract_delimited_string ();
-
- return (extract_delimited_string (string, sindex, "$(", "(", ")"));
- }
-
- /* Extract the $[ construct in STRING, and return a new string.
- Start extracting at (SINDEX) as if we had just seen "$[".
- Make (SINDEX) get the position just after the matching "]".
-
- Strictly speaking, according to the letter of POSIX.2, arithmetic
- substitutions cannot be nested. This code allows nesting, however,
- and it is fully implemented. */
- char *
- extract_arithmetic_subst (string, sindex)
- char *string;
- int *sindex;
- {
- char *extract_delimited_string ();
-
- return (extract_delimited_string (string, sindex, "$[", "[", "]"));
- }
-
- /* Extract and create a new string from the contents of STRING, a
- character string delimited with OPENER and CLOSER. SINDEX is
- the address of an int describing the current offset in STRING;
- it should point to just after the first OPENER found. On exit,
- SINDEX gets the position just after the matching CLOSER. If
- OPENER is more than a single character, ALT_OPENER, if non-null,
- contains a character string that can also match CLOSER and thus
- needs to be skipped. */
- char *
- extract_delimited_string (string, sindex, opener, alt_opener, closer)
- char *string;
- int *sindex;
- char *opener, *alt_opener, *closer;
- {
- register int i, c, l;
- int pass_character, nesting_level;
- int delimiter, delimited_nesting_level;
- int len_closer, len_opener, len_alt_opener;
- char *result;
-
- len_opener = strlen (opener);
- len_alt_opener = alt_opener ? strlen (alt_opener) : 0;
- len_closer = strlen (closer);
-
- pass_character = delimiter = delimited_nesting_level = 0;
-
- nesting_level = 1;
-
- for (i = *sindex; c = string[i]; i++)
- {
- if (pass_character)
- {
- pass_character = 0;
- continue;
- }
-
- if (c == '\\')
- {
- if ((delimiter == '"') &&
- (member (string[i + 1], slashify_in_quotes)))
- {
- pass_character++;
- continue;
- }
- }
-
- if (!delimiter || delimiter == '"')
- {
- if (strncmp (string + i, opener, len_opener) == 0)
- {
- if (!delimiter)
- nesting_level++;
- else
- delimited_nesting_level++;
-
- i += len_opener - 1;
- continue;
- }
-
- if (len_alt_opener &&
- strncmp (string + i, alt_opener, len_alt_opener) == 0)
- {
- if (!delimiter)
- nesting_level++;
- else
- delimited_nesting_level++;
-
- i += len_alt_opener - 1;
- continue;
- }
-
- if (strncmp (string + i, closer, len_closer) == 0)
- {
- i += len_closer - 1;
-
- if (delimiter && delimited_nesting_level)
- delimited_nesting_level--;
-
- if (!delimiter)
- {
- nesting_level--;
- if (nesting_level == 0)
- break;
- }
- }
- }
-
- if (delimiter)
- {
- if (c == delimiter || delimiter == '\\')
- delimiter = 0;
- continue;
- }
- else
- {
- if (c == '"' || c == '\'' || c == '\\')
- delimiter = c;
- }
- }
-
- l = i - *sindex;
- result = (char *)xmalloc (1 + l);
- strncpy (result, &string[*sindex], l);
- result[l] = '\0';
- *sindex = i;
-
- if (!c && (delimiter || nesting_level))
- {
- report_error ("bad substitution: %s%s", opener, result);
- free (result);
- longjmp (top_level, DISCARD);
- }
- return (result);
- }
-
- /* An artifact for extracting the contents of a quoted string. Since the
- string is about to be evaluated, we pass everything through, and only
- strip backslash before backslash or quote. */
- /* This is a mini state machine. */
- char *
- string_extract_double_quoted (string, sindex)
- char *string;
- int *sindex;
- {
- register int c, j, i;
- char *temp; /* The new string we return. */
- int pass_next, backquote; /* State variables for the machine. */
-
- pass_next = backquote = 0;
- temp = (char *)xmalloc (1 + (strlen (string) - *sindex));
-
- for (j = 0, i = *sindex; c = string[i]; i++)
- {
- /* Process a character that was quoted by a backslash. */
- if (pass_next)
- {
- /* Posix.2 sez:
-
- ``The backslash shall retain its special meaning as an escape
- character only when followed by one of the characters:
- $ ` " \ <newline>''.
-
- We handle the double quotes here. expand_word_internal handles
- the rest. */
- if (c != '"')
- temp[j++] = '\\';
- temp[j++] = c;
- pass_next = 0;
- continue;
- }
-
- /* A backslash protects the next character. The code just above
- handles preserving the backslash in front of any character but
- a double quote. */
- if (c == '\\')
- {
- pass_next++;
- continue;
- }
-
- /* Inside backquotes, ``the portion of the quoted string from the
- initial backquote and the characters up to the next backquote
- that is not preceded by a backslash, having escape characters
- removed, defines that command''. */
- if (backquote)
- {
- if (c == '`')
- backquote = 0;
- temp[j++] = c;
- continue;
- }
-
- if (c == '`')
- {
- temp[j++] = c;
- backquote++;
- continue;
- }
-
- /* Pass everything between `$(' and the matching `)' through verbatim. */
- if (c == '$' && string[i + 1] == '(')
- {
- register int t;
- int si;
- char *ret;
-
- si = i + 2;
- ret = extract_delimited_string (string, &si, "$(", "(", ")");
-
- temp[j++] = '$';
- temp[j++] = '(';
-
- for (t = 0; ret[t]; t++)
- temp[j++] = ret[t];
-
- i = si;
- temp[j++] = string[i];
- free (ret);
- continue;
- }
-
- /* An unescaped double quote serves to terminate the string. */
- if (c == '"')
- break;
-
- /* Add the character to the quoted string we're accumulating. */
- temp[j++] = c;
- }
- temp[j] = '\0';
- *sindex = i;
- return (temp);
- }
-
- /* Extract the name of the variable to bind to from the assignment string. */
- char *
- assignment_name (string)
- char *string;
- {
- int offset = assignment (string);
- char *temp;
- if (!offset) return (char *)NULL;
- temp = (char *)xmalloc (offset + 1);
- strncpy (temp, string, offset);
- temp[offset] = '\0';
- return (temp);
- }
-
- /* Return a single string of all the words in LIST. SEP is the separator
- to put between individual elements of LIST in the output string. */
-
- static char *
- string_list_internal (list, sep)
- WORD_LIST *list;
- char *sep;
- {
- char *result = (char *)NULL;
- int sep_len;
-
- sep_len = strlen (sep);
-
- while (list)
- {
- /* Can't simply let xrealloc malloc the bytes for us the first time
- because of the strcat (result, ...) -- we need to make sure result
- is initialized to null after being allocated initially. */
- if (!result)
- result = savestring ("");
-
- result = (char *)xrealloc
- (result, 2 + sep_len + strlen (result) + strlen (list->word->word));
- strcat (result, list->word->word);
- if (list->next)
- strcat (result, sep);
- list = list->next;
- }
- return (result);
- }
-
- /* Return a single string of all the words present in LIST, separating
- each word with a space. */
- char *
- string_list (list)
- WORD_LIST *list;
- {
- return (string_list_internal (list, " "));
- }
-
- /* Return a single string of all the words present in LIST, obeying the
- quoting rules for "$*", to wit: (P1003.2, draft 11, 3.5.2, "If the
- expansion [of $*] appears within a double quoted string, it expands
- to a single field with the value of each parameter separated by the
- first character of the IFS variable, or by a <space> if IFS is unset
- [or null]." */
-
- char *
- string_list_dollar_star (list)
- WORD_LIST *list;
- {
- char *ifs = get_string_value ("IFS");
- char sep[2];
-
- if (!ifs)
- sep[0] = ' ';
- else if (!*ifs)
- sep[0] = '\0';
- else
- sep[0] = *ifs;
-
- sep[1] = '\0';
-
- return (string_list_internal (list, sep));
- }
-
- /* Return the list of words present in STRING. Separate the string into
- words at any of the characters found in SEPARATORS. If QUOTED is
- non-zero then word in the list will have its quoted flag set, otherwise
- the quoted flag is left as make_word () deemed fit.
-
- This obeys the P1003.2 draft 11 word splitting semantics. If `separators'
- is exactly <space><tab><newline>, then the splitting algorithm is that of
- the Bourne shell, which treats any sequence of characters from `separators'
- as a delimiter. If IFS is unset, which results in `separators' being set
- to "", no splitting occurs. If separators has some other value, the
- following rules are applied (`IFS white space' means zero or more
- occurrences of <space>, <tab>, or <newline>, as long as those characters
- are in `separators'):
-
- 1) IFS white space is ignored at the start and the end of the
- string.
- 2) Each occurrence of a character in `separators' that is not
- IFS white space, along with any adjacent occurrences of
- IFS white space delimits a field.
- 3) Any nonzero-length sequence of IFS white space delimits a field.
- */
-
- /* BEWARE! list_string strips null arguments. Don't call it twice and
- expect to have "" preserved! */
-
- /* Is C a quoted NULL character? */
- #define QUOTED_NULL(c) ((unsigned char)(c) == (unsigned char)0x80)
-
- /* Perform quoted null character removal on STRING. */
- void
- remove_quoted_nulls (string)
- char *string;
- {
- register char *s;
-
- for (s = string; s && *s; s++)
- {
- if (QUOTED_NULL (*s))
- {
- strcpy (s, s + 1);
- s--;
- }
- }
- }
-
- /* Perform quoted null character removal on each element of LIST.
- This modifies LIST. */
- word_list_remove_quoted_nulls (list)
- WORD_LIST *list;
- {
- register WORD_LIST *t;
-
- t = list;
-
- while (t)
- {
- remove_quoted_nulls (t->word->word);
- t = t->next;
- }
- }
-
- /* This performs word splitting and quoted null character removal on
- STRING. */
-
- #define issep(c) (member ((c), separators))
- #define spctabnl(c) ((c) == ' '|| (c) == '\t' || (c) == '\n')
-
- WORD_LIST *
- list_string (string, separators, quoted)
- register char *string, *separators;
- int quoted;
- {
- WORD_LIST *result = (WORD_LIST *)NULL;
- char *current_word = (char *)NULL, *s;
- int sindex = 0;
- int sh_style_split;
-
- if (!string || !*string)
- return ((WORD_LIST *)NULL);
-
- sh_style_split = separators && *separators && (!strcmp (separators, " \t\n"));
-
- /* Remove sequences of whitespace at the beginning and end of STRING, as
- long as those characters appear in IFS. */
- for (s = string; *s && spctabnl (*s) && issep (*s); s++);
- if (!*s)
- return ((WORD_LIST *)NULL);
- string = s;
- s += strlen (s) - 1;
- for ( ; s > string && *s && spctabnl (*s) & issep (*s); s--);
- if (!*s)
- return ((WORD_LIST *)NULL);
- *++s = '\0';
-
- /* OK, now STRING points to a word that does not begin with white space.
- The splitting algorithm is:
- extract a word, stopping at a separator
- skip sequences of spc, tab, or nl as long as they are separators
- This obeys the field splitting rules in Posix.2 draft 11.x. */
-
- while (string[sindex])
- {
- current_word = string_extract_verbatim (string, &sindex, separators);
- if (!current_word)
- break;
-
- /* If we have a quoted empty string, add a quoted null argument. We
- want to preserve the quoted null character iff this is a quoted
- empty string; otherwise the quoted null characters are removed
- below. */
- if (QUOTED_NULL (current_word[0]) && current_word[1] == '\0')
- {
- WORD_DESC *t = make_word (" ");
- t->quoted++;
- t->word[0] = (unsigned char)QUOTE_CHAR ('\0');
- result = make_word_list (t, result);
- }
-
- /* If we have something, then add it regardless. */
- else if (strlen (current_word))
- {
- register char *temp_string;
-
- /* Perform quoted null character removal on the current word. */
- for (temp_string = current_word; *temp_string; temp_string++)
- if (QUOTED_NULL (*temp_string))
- {
- strcpy (temp_string, temp_string + 1);
- temp_string--;
- }
-
- result = make_word_list (make_word (current_word), result);
- if (quoted)
- result->word->quoted++;
- }
-
- /* If we're not doing sequences of separators in the traditional
- Bourne shell style, then add a quoted null argument. */
-
- else if (!sh_style_split && !spctabnl (string[sindex]))
- {
- result = make_word_list (make_word (""), result);
- result->word->quoted++;
- }
-
- free (current_word);
-
- /* Move past the current separator character. */
- if (string[sindex])
- sindex++;
-
- /* Now skip sequences of space, tab, or newline characters if they are
- in the list of separators. */
- while (string[sindex] &&
- spctabnl (string[sindex]) &&
- issep (string[sindex]))
- sindex++;
-
- }
- return (WORD_LIST *)reverse_list (result);
- }
-
- /* Given STRING, an assignment string, get the value of the right side
- of the `=', and bind it to the left side. If EXPAND is true, then
- perform parameter expansion, command substitution, and arithmetic
- expansion on the right-hand side. Perform tilde expansion in any
- case. Do not perform word splitting on the result of expansion. */
- do_assignment_internal (string, expand)
- char *string;
- int expand;
- {
- int offset = assignment (string);
- char *name = savestring (string);
- char *value = (char *)NULL;
- SHELL_VAR *entry = (SHELL_VAR *)NULL;
-
- if (name[offset] == '=')
- {
- char *tilde_expand (), *string_list ();
- WORD_LIST *list, *expand_string_unsplit ();
- char *temp;
-
- name[offset] = 0;
- temp = name + offset + 1;
-
- if (expand)
- {
- if (index (temp, '~'))
- temp = tilde_expand (temp);
- else
- temp = savestring (temp);
-
- list = expand_string_unsplit (temp, 0);
-
- if (list)
- {
- value = string_list (list);
- dispose_words (list);
- }
- free (temp);
- }
- else
- value = savestring (temp);
- }
-
- if (!value)
- value = savestring ("");
-
- entry = bind_variable (name, value);
-
- if (echo_command_at_execute)
- {
- extern char *indirection_level_string ();
- fprintf (stderr, "%s%s=%s\n", indirection_level_string (), name, value);
- }
-
- /* Yes, here is where the special shell variables get tested for.
- Don't ask me, I just work here. This is really stupid. I would
- swear, but I've decided that that is an impolite thing to do in
- source that is to be distributed around the net, even if this code
- is totally brain-damaged. */
-
- /* if (strcmp (name, "PATH") == 0) Yeeecchhh!!!*/
- stupidly_hack_special_variables (name);
-
- if (entry)
- entry->attributes &= ~att_invisible;
- if (value)
- free (value);
- free (name);
- }
-
- /* Perform the assignment statement in STRING, and expand the
- right side by doing command and parameter expansion. */
- do_assignment (string)
- char *string;
- {
- do_assignment_internal (string, 1);
- }
-
- /* Given STRING, an assignment string, get the value of the right side
- of the `=', and bind it to the left side. Do not do command and
- parameter substitution on the right hand side. */
- do_assignment_no_expand (string)
- char *string;
- {
- do_assignment_internal (string, 0);
- }
-
- /* Most of the substitutions must be done in parallel. In order
- to avoid using tons of unclear goto's, I have some functions
- for manipulating malloc'ed strings. They all take INDEX, a
- pointer to an integer which is the offset into the string
- where manipulation is taking place. They also take SIZE, a
- pointer to an integer which is the current length of the
- character array for this string. */
-
- /* Append SOURCE to TARGET at INDEX. SIZE is the current amount
- of space allocated to TARGET. SOURCE can be NULL, in which
- case nothing happens. Gets rid of SOURCE by free ()ing it.
- Returns TARGET in case the location has changed. */
- char *
- sub_append_string (source, target, index, size)
- char *source, *target;
- int *index, *size;
- {
- if (source)
- {
- while ((int)strlen (source) >= (int)(*size - *index))
- target = (char *)xrealloc (target, *size += DEFAULT_ARRAY_SIZE);
-
- strcat (target, source);
- *index += strlen (source);
-
- free (source);
- }
- return (target);
- }
-
- /* Append the textual representation of NUMBER to TARGET.
- INDEX and SIZE are as in SUB_APPEND_STRING. */
- char *
- sub_append_number (number, target, index, size)
- int number, *index, *size;
- char *target;
- {
- char *temp = (char *)xmalloc (10);
- sprintf (temp, "%d", number);
- return (sub_append_string (temp, target, index, size));
- }
-
- /* Return the word list that corresponds to `$*'. */
- WORD_LIST *
- list_rest_of_args ()
- {
- register WORD_LIST *list = (WORD_LIST *)NULL;
- register WORD_LIST *args = rest_of_args;
- int i;
-
- for (i = 1; i < 10; i++)
- if (dollar_vars[i])
- list = make_word_list (make_word (dollar_vars[i]), list);
- while (args)
- {
- list = make_word_list (make_word (args->word->word), list);
- args = args->next;
- }
- return ((WORD_LIST *)reverse_list (list));
- }
-
- /* Make a single large string out of the dollar digit variables,
- and the rest_of_args. If DOLLAR_STAR is 1, then obey the special
- case of "$*" with respect to IFS. */
- char *
- string_rest_of_args (dollar_star)
- int dollar_star;
- {
- register WORD_LIST *list = list_rest_of_args ();
- char *string;
-
- if (!dollar_star)
- string = string_list (list);
- else
- string = string_list_dollar_star (list);
-
- dispose_words (list);
- return (string);
- }
-
- /***************************************************
- * *
- * Functions to Expand a String *
- * *
- ***************************************************/
-
- /* Perform parameter expansion, command substitution, and arithmetic
- expansion on STRING, as if it were a word. Leave the result quoted. */
- static WORD_LIST *
- expand_string_internal (string, quoted)
- char *string;
- int quoted;
- {
- WORD_DESC *make_word (), *temp = make_word (string);
- WORD_LIST *tresult, *expand_word_internal ();
-
- tresult = expand_word_internal (temp, quoted, (int *)NULL, (int *)NULL);
- dispose_word (temp);
- return (tresult);
- }
-
- /* Expand STRING by performing parameter expansion, command substitution,
- and arithmetic expansion. Dequote the resulting WORD_LIST before
- returning it, but do not perform word splitting. The call to
- remove_quoted_nulls () is in here because word splitting normally
- takes care of quote removal. */
- WORD_LIST *
- expand_string_unsplit (string, quoted)
- char *string;
- int quoted;
- {
- WORD_LIST *value = expand_string_internal (string, quoted);
-
- if (value && value->word)
- remove_quoted_nulls (value->word->word);
-
- if (value)
- dequote_list (value);
- return (value);
- }
-
- /* Expand STRING just as if you were expanding a word. This also returns
- a list of words. Note that filename globbing is *NOT* done for word
- or string expansion, just when the shell is expanding a command. This
- does parameter expansion, command substitution, arithmetic expansion,
- and word splitting. Dequote the resultant WORD_LIST before returning. */
- WORD_LIST *
- expand_string (string, quoted)
- char *string;
- int quoted;
- {
- WORD_LIST *value = expand_string_internal (string, quoted);
- WORD_LIST *result, *word_list_split ();
-
- result = word_list_split (value);
- dispose_words (value);
- if (result)
- dequote_list (result);
- return (result);
- }
-
- /* Expand STRING just as if you were expanding a word, but do not dequote
- the resultant WORD_LIST. This is called only from within this file,
- and is used to correctly preserve quoted characters when expanding
- things like ${1+"$@"}. This does parameter expansion, command
- subsitution, arithmetic expansion, and word splitting. */
- static WORD_LIST *
- expand_string_leave_quoted (string, quoted)
- char *string;
- int quoted;
- {
- WORD_LIST *tlist = expand_string_internal (string, quoted);
- WORD_LIST *tresult, *word_list_split ();
-
- tresult = word_list_split (tlist);
- dispose_words (tlist);
- return (tresult);
- }
-
- /***************************************************
- * *
- * Functions to handle quoting chars *
- * *
- ***************************************************/
-
- /* I'm going to have to rewrite expansion because filename globbing is
- beginning to make the entire arrangement ugly. I'll do this soon. */
- dequote_list (list)
- register WORD_LIST *list;
- {
- register char *s;
-
- while (list)
- {
- s = dequote_string (list->word->word);
- free (list->word->word);
- list->word->word = s;
- list = list->next;
- }
- }
-
- /* Quote the string S. Return a new string. */
- static char *
- quote_string (s)
- char *s;
- {
- unsigned char *result;
-
- /* If S is an empty string then simply create a string consisting of a
- quoted null. */
- if (s[0] == '\0')
- {
- result = (unsigned char *)xmalloc (2);
- result[0] = (unsigned char)QUOTE_CHAR ('\0');
- result[1] = '\0';
- }
- else
- {
- register unsigned char *t;
- result = (unsigned char *)savestring (s);
- for (t = result; t && *t ; t++)
- *t |= 0x80;
- }
- return ((char *)result);
- }
-
- /* De-quoted quoted characters in string s. */
- static char *
- dequote_string (s)
- char *s;
- {
- register unsigned char *t;
- unsigned char *result;
-
- result = (unsigned char *)savestring (s);
- for (t = result; t && *t ; t++)
- *t = DEQUOTE_CHAR (*t);
-
- return ((char *)result);
- }
-
- /* Quote the entire WORD_LIST list. */
- static void
- quote_list (list)
- WORD_LIST *list;
- {
- register WORD_LIST *w;
-
- for (w = list; w; w = w->next)
- {
- char *t = w->word->word;
- w->word->word = quote_string (t);
- free (t);
- w->word->quoted = 1;
- }
- }
-
- /* **************************************************************** */
- /* */
- /* Functions for Removing Patterns */
- /* */
- /* **************************************************************** */
-
- /* Remove the portion of PARAM matched by PATTERN according to OP, where OP
- can have one of 4 values:
- RP_LONG_LEFT remove longest matching portion at start of PARAM
- RP_SHORT_LEFT remove shortest matching portion at start of PARAM
- RP_LONG_RIGHT remove longest matching portion at end of PARAM
- RP_SHORT_RIGHT remove shortest matching portion at end of PARAM
- */
-
- #define RP_LONG_LEFT 1
- #define RP_SHORT_LEFT 2
- #define RP_LONG_RIGHT 3
- #define RP_SHORT_RIGHT 4
-
- static char *
- remove_pattern (param, pattern, op)
- char *param, *pattern;
- int op;
- {
- register int len = param ? strlen (param) : 0;
- register char *end = param + len;
- register char *p, *ret, c;
-
- if (pattern == NULL || *pattern == '\0') /* minor optimization */
- return (savestring (param));
-
- if (param == NULL || *param == '\0')
- return (param);
-
- switch (op)
- {
- case RP_LONG_LEFT: /* remove longest match at start */
- for (p = end; p >= param; p--)
- {
- c = *p; *p = '\0';
- if (fnmatch (pattern, param, 0) != FNM_NOMATCH)
- {
- *p = c;
- return (savestring (p));
- }
- *p = c;
- }
- break;
-
- case RP_SHORT_LEFT: /* remove shortest match at start */
- for (p = param; p <= end; p++)
- {
- c = *p; *p = '\0';
- if (fnmatch (pattern, param, 0) != FNM_NOMATCH)
- {
- *p = c;
- return (savestring (p));
- }
- *p = c;
- }
- break;
-
- case RP_LONG_RIGHT: /* remove longest match at end */
- for (p = param; p <= end; p++)
- {
- if (fnmatch (pattern, p, 0) != FNM_NOMATCH)
- {
- c = *p;
- *p = '\0';
- ret = savestring (param);
- *p = c;
- return (ret);
- }
- }
- break;
-
- case RP_SHORT_RIGHT: /* remove shortest match at end */
- for (p = end; p >= param; p--)
- {
- if (fnmatch (pattern, p, 0) != FNM_NOMATCH)
- {
- c = *p;
- *p = '\0';
- ret = savestring (param);
- *p = c;
- return (ret);
- }
- }
- break;
- }
- return (savestring (param)); /* no match, return original string */
- }
-
- /*******************************************
- * *
- * Functions to expand WORD_DESCs *
- * *
- *******************************************/
-
- /* Expand WORD, performing word splitting on the result. This does
- parameter expansion, command substitution, arithmetic expansion,
- word splitting, and quote removal. */
-
- WORD_LIST *
- expand_word (word, quoted)
- WORD_DESC *word;
- int quoted;
- {
- WORD_LIST *word_list_split (), *expand_word_internal ();
- WORD_LIST *result, *tresult;
-
- tresult = expand_word_internal (word, quoted, (int *)NULL, (int *)NULL);
- result = word_list_split (tresult);
- dispose_words (tresult);
- if (result)
- dequote_list (result);
- return (result);
- }
-
- /* Expand WORD, but do not perform word splitting on the result. This
- does parameter expansion, command substitution, arithmetic expansion,
- and quote removal. */
- WORD_LIST *
- expand_word_no_split (word, quoted)
- WORD_DESC *word;
- int quoted;
- {
- WORD_LIST *expand_word_internal ();
- WORD_LIST *result;
-
- result = expand_word_internal (word, quoted, (int *)NULL, (int *)NULL);
- if (result)
- dequote_list (result);
- return (result);
- }
-
- WORD_LIST *
- expand_word_leave_quoted (word, quoted)
- WORD_DESC *word;
- int quoted;
- {
- WORD_LIST *expand_word_internal (), *result;
-
- result = expand_word_internal (word, quoted, (int *)NULL, (int *)NULL);
- return (result);
- }
-
- /* Return the value of a positional parameter. This handles values > 10. */
- char *
- get_dollar_var_value (ind)
- int ind;
- {
- char *temp;
-
- if (ind < 10)
- {
- if (dollar_vars[ind])
- temp = savestring (dollar_vars[ind]);
- else
- temp = (char *)NULL;
- }
- else /* We want something like ${11} */
- {
- WORD_LIST *p = rest_of_args;
-
- ind -= 10;
- while (p && ind--)
- p = p->next;
- if (p)
- temp = savestring (p->word->word);
- else
- temp = (char *)NULL;
- }
- return (temp);
- }
-
- /* Perform command substitution on STRING. This returns a string,
- possibly quoted. */
- static char *
- command_substitute (string, quoted)
- char *string;
- int quoted;
- {
- pid_t pid, old_pid;
- int fildes[2];
- char *istring = (char *)NULL;
- int istring_index, istring_size, c = 1;
- extern int interactive, last_command_exit_value;
-
- istring_index = istring_size = 0;
-
- /* Don't fork () if there is no need to. In the case of no command to
- run, just return NULL. */
- if (!string || !*string)
- return ((char *)NULL);
-
- /* Pipe the output of executing STRING into the current shell. */
- if (pipe (fildes) < 0)
- {
- report_error ("Can't make pipes for command substitution!");
- goto error_exit;
- }
-
- old_pid = last_made_pid;
- #if defined (JOB_CONTROL)
- {
- pid_t old_pipeline_pgrp = pipeline_pgrp;
-
- pipeline_pgrp = shell_pgrp;
- pid = make_child (savestring ("command substitution"), 0);
-
- stop_making_children ();
- pipeline_pgrp = old_pipeline_pgrp;
- }
- #else /* JOB_CONTROL */
- pid = make_child (savestring ("command substitution"), 0);
- #endif /* JOB_CONTROL */
-
- if (pid < 0)
- {
- report_error ("Can't make a child for command substitution!");
- error_exit:
- if (istring)
- free (istring);
- return ((char *)NULL);
- }
-
- if (pid == 0)
- {
- #if defined (JOB_CONTROL)
- set_job_control (0);
- #endif
- if (dup2 (fildes[1], 1) < 0)
- {
- extern int errno;
- report_error ("command_substitute: cannot duplicate pipe as fd 1: %s\n",
- strerror (errno));
- exit (EXECUTION_FAILURE);
- }
- close (fildes[1]);
- /* If standard output is closed in the parent shell
- (such as after `exec >&-'), file descriptor 1 will be
- the lowest available file descriptor, and end up in
- fildes[0]. This can happen for stdin and stderr as well,
- but stdout is more important -- it will cause no output
- to be generated from this command. */
- if (fildes[0] > 2)
- close (fildes[0]);
- interactive = 0;
-
- exit (parse_and_execute (string, "command substitution"));
- }
- else
- {
- FILE *istream;
-
- istream = fdopen (fildes[0], "r");
-
- #if defined (JOB_CONTROL) && defined (PGRP_PIPE)
- close_pgrp_pipe ();
- #endif /* JOB_CONTROL && PGRP_PIPE */
-
- close (fildes[1]);
-
- if (!istream)
- {
- report_error ("Can't reopen pipe to command substitution");
- goto error_exit;
- }
-
- /* Read the output of the command through the pipe. */
- while (1)
- {
- #if defined (USG) || (defined (_POSIX_VERSION) && defined (Ultrix))
- c = sysv_getc (istream);
- #else
- c = getc (istream);
- #endif
-
- if (c == EOF)
- break;
-
- /* Add the character to ISTRING. */
- while (istring_index + 1 >= istring_size)
- istring = (char *) xrealloc
- (istring, istring_size += DEFAULT_ARRAY_SIZE);
-
- if (quoted)
- istring[istring_index++] = QUOTE_CHAR (c);
- else
- istring[istring_index++] = c;
-
- istring[istring_index] = '\0';
- }
-
- fclose (istream);
- close (fildes[0]);
-
- last_command_exit_value = wait_for (pid);
- last_command_subst_pid = pid;
- last_made_pid = old_pid;
-
- #if defined (JOB_CONTROL)
- /* If last_command_exit_value > 128, then the substituted command
- was terminated by a signal. If that signal was SIGINT, then send
- SIGINT to ourselves. This will break out of loops, for instance. */
- if (last_command_exit_value == (128 + SIGINT))
- kill (getpid (), SIGINT);
-
- /* wait_for gives the terminal back to shell_pgrp. If some other
- process group should have it, give it away to that group here. */
- if (pipeline_pgrp != (pid_t)0)
- give_terminal_to (pipeline_pgrp);
- #endif /* JOB_CONTROL */
-
- /* If we read no output, just return now and save ourselves some
- trouble. */
- if (istring_index == 0)
- goto error_exit;
-
- /* Strip trailing newlines from the output of the command. */
- if (quoted)
- {
- while (istring_index > 0 &&
- DEQUOTE_CHAR (istring[istring_index - 1]) == '\n')
- --istring_index;
-
- istring[istring_index] = '\0';
- }
- else
- {
- strip_trailing (istring, 1);
- istring_index = strlen (istring);
- }
-
- return (istring);
- }
- }
-
- /********************************************************
- * *
- * Utility functions for parameter expansion *
- * *
- ********************************************************/
-
- /* Handle removing a pattern from a string as a result of ${name%[%]value}
- or ${name#[#]value}. */
- static char *
- parameter_brace_remove_pattern (value, temp, c)
- char *value, *temp;
- int c;
- {
- int pattern_specifier;
- WORD_LIST *l;
- char *pattern, *t;
-
- if (c == '#')
- {
- if (*value == '#')
- {
- value++;
- pattern_specifier = RP_LONG_LEFT;
- }
- else
- pattern_specifier = RP_SHORT_LEFT;
- }
- else /* c == '%' */
- {
- if (*value == '%')
- {
- value++;
- pattern_specifier = RP_LONG_RIGHT;
- }
- else
- pattern_specifier = RP_SHORT_RIGHT;
- }
-
- l = expand_string (value, 0);
- pattern = (char *)string_list (l);
- dispose_words (l);
- t = remove_pattern (temp, pattern, pattern_specifier);
- free (pattern);
- return (t);
- }
-
- /* Parameter expand NAME, and return a new string which is the expansion,
- or NULL if there was no expansion.
- VAR_IS_SPECIAL is non-zero if NAME is one of the special variables in
- the shell, e.g., "@", "$", "*", etc. QUOTED, if non-zero, means that
- NAME was found inside of a double-quoted expression. */
- static char *
- parameter_brace_expand_word (name, var_is_special, quoted)
- char *name;
- int var_is_special, quoted;
- {
- char *temp = (char *)NULL;
-
- /* Handle multiple digit arguments, as in ${11}. */
- if (digit (*name))
- {
- int arg_index = atoi (name);
-
- temp = get_dollar_var_value (arg_index);
- }
- else if (var_is_special) /* ${@} */
- {
- char *tt;
- WORD_LIST *l;
-
- tt = (char *)alloca (2 + strlen (name));
- tt[0] = '$'; tt[1] = '\0';
- strcat (tt, name);
- l = expand_string_leave_quoted (tt, quoted);
- temp = string_list (l);
- dispose_words (l);
- }
- else
- {
- SHELL_VAR *var = find_variable (name);
-
- if (var && !invisible_p (var) && (temp = value_cell (var)))
- temp = savestring (temp);
- }
- return (temp);
- }
-
- /* Expand the right side of a parameter expansion of the form ${NAMEcVALUE},
- depending on the value of C, the separating character. C can be one of
- "-", "+", or "=". */
- static char *
- parameter_brace_expand_rhs (name, value, c, quoted)
- char *name, *value;
- int c;
- {
- extern char *tilde_expand ();
- WORD_LIST *l;
- char *t, *t1, *temp;
-
- if (value[0] == '~' ||
- (index (value, '~') && unquoted_substring ("=~", value)))
- temp = tilde_expand (value);
- else
- temp = savestring (value);
-
- l = expand_string_leave_quoted (temp, quoted);
- free (temp);
-
- temp = (char *)string_list (l);
- dispose_words (l);
-
- if (c == '-' || c == '+')
- return (temp);
-
- /* c == '=' */
- if (temp)
- t = savestring (temp);
- else
- t = savestring ("");
- t1 = dequote_string (t);
- free (t);
- t = t1;
- bind_variable (name, t);
- free (t);
- return (temp);
- }
-
- /* Deal with the right hand side of a ${name:?value} expansion in the case
- that NAME is null or not set. If VALUE is non-null it is expanded and
- used as the error message to print, otherwise a standard message is
- printed. */
- static void
- parameter_brace_expand_error (name, value)
- char *name, *value;
- {
- extern int interactive;
-
- if (value && *value)
- {
- WORD_LIST *l = expand_string (value, 0);
- char *temp1 = string_list (l);
- fprintf (stderr, "%s: %s\n", name, temp1 ? temp1 : value);
- if (temp1)
- free (temp1);
- dispose_words (l);
- }
- else
- report_error ("%s: parameter null or not set", name);
-
- /* Free the data we have allocated during this expansion, since we
- are about to longjmp out. */
- free (name);
- if (value)
- free (value);
-
- if (!interactive)
- longjmp (top_level, FORCE_EOF);
- else
- longjmp (top_level, DISCARD);
- }
-
- /* Handle the parameter brace expansion that requires us to return the
- length of a parameter. */
- static int
- parameter_brace_expand_length (name)
- char *name;
- {
- char *t;
- int number = 0;
-
- if (name[1] == '\0') /* ${#} */
- {
- WORD_LIST *l = list_rest_of_args ();
- number = list_length (l);
- dispose_words (l);
- }
- else if (name[1] != '*' && name[1] != '@')
- {
- number = 0;
-
- if (digit (name[1])) /* ${#1} */
- {
- if (t = get_dollar_var_value (atoi (&name[1])))
- {
- number = strlen (t);
- free (t);
- }
- }
- else /* ${#PS1} */
- {
- WORD_LIST *list;
- char *newname;
-
- newname = savestring (name);
- newname[0] = '$';
- list = expand_string (newname, 0);
- t = string_list (list);
- free (newname);
- dispose_words (list);
-
- if (t)
- number = strlen (t);
- }
- }
- else /* ${#@} and ${#*} */
- {
- if (t = string_rest_of_args (1))
- {
- number = strlen (t);
- free (t);
- }
- }
- return (number);
- }
-
- /* Make a word list which is the parameter and variable expansion,
- command substitution, arithmetic substitution, and quote removed
- expansion of WORD. Return a pointer to a WORD_LIST which is the
- result of the expansion. If WORD contains a null word, the word
- list returned is also null.
-
- QUOTED, when non-zero specifies that the text of WORD is treated
- as if it were surrounded by double quotes.
- CONTAINS_DOLLAR_AT and EXPANDED_SOMETHING are return values; when non-null
- they point to an integer value which receives information about expansion.
- CONTAINS_DOLLAR_AT gets non-zero if WORD contained "$@", else zero.
- EXPANDED_SOMETHING get non-zero if WORD contained any parameter expansions,
- else zero.
-
- This only does word splitting in the case of $@ expansion. In that
- case, we split on ' '. */
- WORD_LIST *
- expand_word_internal (word, quoted, contains_dollar_at, expanded_something)
- WORD_DESC *word;
- int quoted;
- int *contains_dollar_at;
- int *expanded_something;
- {
- extern char *itos ();
- extern int last_command_exit_value;
-
- /* The thing that we finally output. */
- WORD_LIST *result = (WORD_LIST *)NULL;
-
- /* The intermediate string that we build while expanding. */
- char *istring = (char *)xmalloc (DEFAULT_ARRAY_SIZE);
-
- /* The current size of the above object. */
- int istring_size = DEFAULT_ARRAY_SIZE;
-
- /* Index into ISTRING. */
- int istring_index = 0;
-
- /* Temporary string storage. */
- char *temp = (char *)NULL;
-
- /* The text of WORD. */
- register char *string = word->word;
-
- /* The index into STRING. */
- register int sindex = 0;
-
- /* This gets 1 if we see a $@ while quoted. */
- int quoted_dollar_at = 0;
-
- /* This gets 1 if we are to treat backslashes as if we are within double
- quotes, but not otherwise behave as if the word is quoted. This is
- used for things like expansion of patterns in case statement pattern
- lists. This is a private variable, but the incoming value of
- Q_KEEP_BACKSLASH is passed to recursive invocations of this function. */
- int preserve_backslashes = 0;
-
- register int c; /* Current character. */
- int number; /* Temporary number value. */
- int t_index; /* For calls to string_extract_xxx. */
- extern int interactive;
- char *command_subst_result; /* For calls to command_substitute (). */
-
- istring[0] = '\0';
-
- if (!string) goto final_exit;
-
- if (quoted & Q_KEEP_BACKSLASH)
- {
- preserve_backslashes = 1;
- quoted &= ~Q_KEEP_BACKSLASH;
- }
-
- if (contains_dollar_at)
- *contains_dollar_at = 0;
-
- /* Begin the expansion. */
-
- for (;;) {
-
- c = string[sindex];
-
- switch (c) { /* Case on toplevel character. */
-
- case '\0':
- goto finished_with_string;
-
- case '$':
-
- if (expanded_something)
- *expanded_something = 1;
-
- c = string[++sindex];
-
- /* Do simple cases first. Switch on what follows '$'. */
- switch (c)
- {
- /* $0 .. $9? */
- case '0':
- case '1':
- case '2':
- case '3':
- case '4':
- case '5':
- case '6':
- case '7':
- case '8':
- case '9':
- if (dollar_vars[digit_value (c)])
- temp = savestring (dollar_vars[digit_value (c)]);
- else
- temp = (char *)NULL;
- goto dollar_add_string;
-
- case '$': /* $$ -- pid of the invoking shell. */
- {
- extern int dollar_dollar_pid;
- number = dollar_dollar_pid;
- }
- add_number:
- temp = itos (number);
- dollar_add_string:
- if (string[sindex]) sindex++;
-
- /* Add TEMP to ISTRING. */
- add_string:
- istring =
- sub_append_string (temp, istring, &istring_index, &istring_size);
- break;
-
- /* $# -- number of positional parameters. */
- case '#':
- {
- WORD_LIST *list = list_rest_of_args ();
- number = list_length (list);
- dispose_words (list);
- goto add_number;
- }
-
- /* $? -- return value of the last synchronous command. */
- case '?':
- number = last_command_exit_value;
- goto add_number;
-
- /* $- -- flags supplied to the shell on invocation or by `set'. */
- case '-':
-
- temp = (char *)which_set_flags ();
- goto dollar_add_string;
-
- /* $! -- Pid of the last asynchronous command. */
- case '!':
- {
- number = (int)last_asynchronous_pid;
-
- /* If no asynchronous pids have been created, echo nothing. */
- if (number == (int)NO_PID)
- {
- if (string[sindex])
- sindex++;
- if (expanded_something)
- *expanded_something = 0;
- break;
- }
- goto add_number;
- }
-
- /* The only difference between this and $@ is when the
- arg is quoted. */
- case '*': /* `$*' */
- temp = string_rest_of_args (quoted);
-
- /* In the case of a quoted string, quote the entire arg-list.
- "$1 $2 $3". */
- if (quoted && temp)
- {
- char *james_brown = temp;
- temp = quote_string (temp);
- free (james_brown);
- }
- goto dollar_add_string;
-
- /* When we have "$@" what we want is "$1" "$2" "$3" ... This
- means that we have to turn quoting off after we split into
- the individually quoted arguments so that the final split
- on the first character of $IFS is still done. */
- case '@': /* `$@' */
- {
- WORD_LIST *tlist = list_rest_of_args ();
- if (quoted && tlist)
- quote_list (tlist);
-
- /* We want to flag the fact that we saw this. We can't turn off
- quoting entirely, because other characters in the string might
- need it (consider "\"$@\""), but we need some way to signal
- that the final split on the first character of $IFS should be
- done, even though QUOTED is 1. */
- if (quoted)
- quoted_dollar_at = 1;
- if (contains_dollar_at)
- *contains_dollar_at = 1;
- temp = string_list (tlist);
- goto dollar_add_string;
- }
-
- /* ${[#]name[[:]#[#]%[%]-=?+[word]]} */
- case '{':
- {
- int check_nullness = 0;
- int var_is_set = 0;
- int var_is_null = 0;
- int var_is_special = 0;
- char *name, *value;
-
- sindex++;
- t_index = sindex;
- name = string_extract (string, &t_index, "#%:-=?+}");
-
- /* If the name really consists of a special variable, then
- make sure that we have the entire name. */
- if (sindex == t_index &&
- (string[sindex] == '-' ||
- string[sindex] == '?' ||
- string[sindex] == '#'))
- {
- char *tt;
- t_index++;
- free (name);
- tt = (string_extract (string, &t_index, "#%:-=?+}"));
- name = (char *)xmalloc (2 + (strlen (tt)));
- *name = string[sindex];
- strcpy (name + 1, tt);
- free (tt);
- }
- sindex = t_index;
-
- /* Find out what character ended the variable name. Then
- do the appropriate thing. */
-
- if (c = string[sindex])
- sindex++;
-
- if (c == ':')
- {
- check_nullness++;
- if (c = string[sindex])
- sindex++;
- }
-
- /* Determine the value of this variable. */
- if (digit (*name) ||
- (strlen (name) == 1 && member (*name, "#-?$!@*")))
- var_is_special++;
-
- /* Check for special expansion things. */
- if (*name == '#')
- {
- /* Handle ${#-} and ${#?}. They return the lengths of
- $- and $?, respectively. */
- if (string[sindex] == '}' &&
- !name[1] &&
- !check_nullness &&
- (c == '-' || c == '?'))
- {
- char *s;
-
- free (name);
-
- if (c == '-')
- s = (char *)which_set_flags ();
- else
- s = itos (last_command_exit_value);
-
- number = s ? strlen (s) : 0;
- if (s)
- free (s);
- goto add_number;
- }
-
- /* Don't allow things like ${#:-foo} to go by; they are
- errors. If we are not pointing at the character just
- after the closing brace, then we haven't gotten all of
- the name. Since it begins with a special character,
- this is a bad substitution. Explicitly check for ${#:},
- which the rules do not catch. */
- if (string[sindex - 1] != '}' || member (c, "?-=+") ||
- (string[sindex - 1] == '}' && !name[1] && c == '}' &&
- check_nullness))
- {
- free (name);
- name = string;
- goto bad_substitution;
- }
-
- number = parameter_brace_expand_length (name);
- /* We are pointing one character after the brace which
- closes this expression. Since the code at add_number
- increments SINDEX, we back up a single character here. */
- sindex--;
- goto add_number;
- }
-
- /* ${@} is identical to $@. */
- if (name[0] == '@' && name[1] == '\0')
- {
- if (quoted)
- quoted_dollar_at = 1;
-
- if (contains_dollar_at)
- *contains_dollar_at = 1;
- }
-
- temp = parameter_brace_expand_word (name, var_is_special, quoted);
-
- if (temp)
- var_is_set++;
-
- if (!var_is_set || !temp || !*temp)
- var_is_null++;
-
- if (!check_nullness)
- var_is_null = 0;
-
- /* Get the rest of the stuff inside the braces. */
- if (c && c != '}')
- {
- /* Scan forward searching for last `{'. This is a hack,
- it will always be a hack, and it always has been a hack. */
- t_index = sindex;
- value = extract_delimited_string (string, &t_index,
- "{", (char *)NULL, "}");
- sindex = t_index;
-
- if (string[sindex] == '}')
- sindex++;
- else
- {
- if (value)
- free (value);
-
- free (name);
- name = string;
- goto bad_substitution;
- }
- }
- else
- value = (char *)NULL;
-
- /* Do the right thing based on which character ended the variable
- name. */
- switch (c)
- {
- case '\0':
- bad_substitution:
- report_error ("%s: bad substitution", name ? name : "??");
- free (name);
- longjmp (top_level, DISCARD);
-
- case '}':
- break;
-
- case '#': /* ${param#[#]pattern} */
- case '%': /* ${param%[%]pattern} */
- {
- char *t;
- if (!value || !*value || !temp || !*temp)
- break;
- t = parameter_brace_remove_pattern (value, temp, c);
- free (temp);
- free (value);
- temp = t;
- }
- break;
-
- case '-':
- case '=':
- case '?':
- case '+':
- if (var_is_set && !var_is_null)
- {
- /* We don't want the value of the named variable for
- anything, just the value of the right hand side. */
- if (c == '+')
- {
- if (temp)
- free (temp);
- if (value)
- temp = parameter_brace_expand_rhs (name, value, c, quoted);
- else
- temp = (char *)NULL;
- }
- /* Otherwise do nothing. Just use the value in temp. */
- }
- else /* var not set or var is null */
- {
- if (temp)
- free (temp);
- temp = (char *)NULL;
- if (c == '=' && var_is_special)
- {
- report_error ("$%s: cannot assign in this way", name);
- free (name);
- free (value);
- longjmp (top_level, DISCARD);
- }
- else if (c == '?')
- parameter_brace_expand_error (name, value);
- else if (c != '+')
- temp =
- parameter_brace_expand_rhs (name, value, c, quoted);
- free (value);
- }
- break;
- } /* end case on closing character. */
- free (name);
- goto add_string;
- } /* end case '{' */
- /* break; */
-
- case '(': /* Do command or arithmetic substitution. */
- /* We have to extract the contents of this paren substitution. */
- {
- char *extract_command_subst ();
- int old_index = ++sindex;
-
- temp = extract_command_subst (string, &old_index);
- sindex = old_index;
-
- /* For the Posix.2-style $(( )) form of arithmetic substitution,
- extract the expression and pass it to the evaluator. */
- if (temp && *temp == '(')
- {
- char *t = temp + 1;
- int last = strlen (t) - 1;
- extern long evalexp ();
-
- if (t[last] != ')')
- {
- report_error ("%s: bad arithmetic substitution", temp);
- free (temp);
- /* XXX - these are mem leaks */
- longjmp (top_level, DISCARD);
- }
-
- /* Cut off ending `)' */
- t[last] = '\0';
-
- number = (int)evalexp (t);
- free (temp);
- goto add_number;
- }
-
- goto handle_command_substitution;
- }
-
- /* Do straight arithmetic substitution. */
- case '[':
- /* We have to extract the contents of this
- arithmetic substitution. */
- {
- char *extract_arithmetic_subst (), *t;
- int old_index = ++sindex;
- WORD_LIST *l;
- extern long evalexp ();
- extern char *this_command_name;
-
- temp = extract_arithmetic_subst (string, &old_index);
- sindex = old_index;
-
- /* Do initial variable expansion. */
- l = expand_string (temp, 1);
- t = string_list (l);
- dispose_words (l);
-
- /* No error messages. */
- this_command_name = (char *)NULL;
- number = (int)evalexp (t);
- free (t);
-
- goto add_number;
- }
-
- default:
- {
- /* Find the variable in VARIABLE_LIST. */
- int old_index = sindex;
- char *name;
- SHELL_VAR *var;
-
- temp = (char *)NULL;
-
- for (;
- (c = string[sindex]) &&
- (isletter (c) || digit (c) || c == '_');
- sindex++);
- name = (char *)substring (string, old_index, sindex);
-
- /* If this isn't a variable name, then just output the `$'. */
- if (!name || !*name)
- {
- free (name);
- temp = savestring ("$");
- if (expanded_something)
- *expanded_something = 0;
- goto add_string;
- }
-
- /* If the variable exists, return its value cell. */
- var = find_variable (name);
-
- if (var && !invisible_p (var) && value_cell (var))
- {
- temp = savestring (value_cell (var));
- free (name);
- goto add_string;
- }
- else
- temp = (char *)NULL;
-
- if (unbound_vars_is_error)
- report_error ("%s: unbound variable", name);
- else
- goto add_string;
-
- free (name);
- longjmp (top_level, DISCARD);
- }
- }
- break; /* End case '$': */
-
- case '`': /* Backquoted command substitution. */
- {
- sindex++;
-
- if (expanded_something)
- *expanded_something = 1;
-
- t_index = sindex;
- temp = string_extract (string, &t_index, "`");
- sindex = t_index;
- de_backslash (temp);
-
- handle_command_substitution:
- command_subst_result = command_substitute (temp, quoted);
-
- if (temp)
- free (temp);
-
- temp = command_subst_result;
-
- if (string[sindex])
- sindex++;
-
- goto add_string;
- }
-
- case '\\':
- if (string[sindex + 1] == '\n')
- {
- sindex += 2;
- continue;
- }
- else
- {
- char *slashify_chars = "";
-
- c = string[++sindex];
-
- if (quoted == Q_HERE_DOCUMENT)
- slashify_chars = slashify_in_here_document;
- else if (quoted == Q_DOUBLE_QUOTES)
- slashify_chars = slashify_in_quotes;
-
- if (preserve_backslashes || (quoted && !member (c, slashify_chars)))
- {
- temp = (char *)xmalloc (3);
- temp[0] = '\\'; temp[1] = c; temp[2] = '\0';
- if (c)
- sindex++;
- goto add_string;
- }
- else
- {
- /* This character is quoted, so add it in quoted mode. */
- c = QUOTE_CHAR (c);
- goto add_character;
- }
- }
-
- case '"':
- if (quoted)
- goto add_character;
- sindex++;
- {
- WORD_LIST *tresult = (WORD_LIST *)NULL;
-
- t_index = sindex;
- temp = string_extract_double_quoted (string, &t_index);
- sindex = t_index;
-
- if (string[sindex])
- sindex++;
-
- if (temp && *temp)
- {
- int dollar_at_flag;
- int quoting_flags = Q_DOUBLE_QUOTES;
- WORD_DESC *temp_word = make_word (temp);
-
- free (temp);
-
- if (preserve_backslashes)
- quoting_flags |= Q_KEEP_BACKSLASH;
- tresult = expand_word_internal (temp_word, quoting_flags,
- &dollar_at_flag, (int *)NULL);
-
- dispose_word (temp_word);
-
- if (!tresult && dollar_at_flag)
- break;
- /* If we get "$@", we know we have expanded something, so we
- need to remember it for the final split on $IFS. This is
- a special case; it's the only case where a quoted string
- can expand into more than one word. It's going to come back
- from the above call to expand_word_internal as a list with
- a single word, in which all characters are quoted and
- separated by blanks. What we want to do is to turn it back
- into a list for the next piece of code. */
- dequote_list (tresult);
- if (dollar_at_flag)
- quoted_dollar_at++;
- if (expanded_something)
- *expanded_something = 1;
- }
- else
- {
- /* What we have is "". This is a minor optimization. */
- free (temp);
- tresult = (WORD_LIST *)NULL;
- }
-
- /* The code above *might* return a list (consider the case of "$@",
- where it returns "$1", "$2", etc.). We can't throw away the rest
- of the list, and we have to make sure each word gets added as
- quoted. We test on tresult->next: if it is non-NULL, we quote
- the whole list, save it to a string with string_list, and add that
- string. We don't need to quote the results of this (and it would be
- wrong, since that would quote the separators as well), so we go
- directly to add_string. */
- if (tresult)
- {
- if (tresult->next)
- {
- quote_list (tresult);
- temp = string_list (tresult);
- dispose_words (tresult);
- goto add_string;
- }
- else
- {
- temp = savestring (tresult->word->word);
- dispose_words (tresult);
- }
- }
- else
- temp = (char *)NULL;
-
- add_quoted_string:
-
- if (temp)
- {
- char *t = temp;
- temp = quote_string (temp);
- free (t);
- }
- else
- {
- /* Add NULL arg. */
- temp = savestring (" ");
- temp[0] = (unsigned char)QUOTE_CHAR ('\0');
- }
- goto add_string;
- }
- /* break; */
-
- case '\'':
- {
- if (!quoted)
- {
- sindex++;
-
- t_index = sindex;
- temp = string_extract_verbatim (string, &t_index, "'");
- sindex = t_index;
-
- if (string[sindex])
- sindex++;
-
- if (!*temp)
- {
- free (temp);
- temp = (char *)NULL;
- }
-
- goto add_quoted_string;
- }
- else
- goto add_character;
-
- break;
- }
-
- default:
-
- /* This is the fix for " $@ " */
- if (quoted)
- c = QUOTE_CHAR (c);
-
- add_character:
- while (istring_index + 1 >= istring_size)
- istring = (char *)
- xrealloc (istring, istring_size += DEFAULT_ARRAY_SIZE);
- istring[istring_index++] = c;
- istring[istring_index] = '\0';
-
- /* Next character. */
- sindex++;
- }
- }
-
- finished_with_string:
- final_exit:
- /* OK, we're ready to return. If we have a quoted string, and
- quoted_dollar_at is not set, we do no splitting at all; otherwise
- we split on ' '. The routines that call this will handle what to
- do if nothing has been expanded. */
- if (istring)
- {
- WORD_LIST *temp_list;
-
- if (quoted_dollar_at)
- temp_list = list_string (istring, " ", quoted);
- else if (*istring)
- {
- temp_list = make_word_list (make_word (istring), (WORD_LIST *)NULL);
- temp_list->word->quoted = quoted;
- }
- else
- temp_list = (WORD_LIST *)NULL;
- free (istring);
- result = (WORD_LIST *)list_append (reverse_list (result), temp_list);
- }
- else
- result = (WORD_LIST *)NULL;
-
- return (result);
- }
-
- /* **************************************************************** */
- /* */
- /* Functions for Quote Removal */
- /* */
- /* **************************************************************** */
-
- /* Perform quote removal on STRING. If QUOTED > 0, assume we are obeying the
- backslash quoting rules for within double quotes. */
- char *
- string_quote_removal (string, quoted)
- char *string;
- int quoted;
- {
- char *r, *result_string, *temp, *temp1;
- int sindex, tindex, c;
-
- /* The result can be no longer than the original string. */
- r = result_string = xmalloc (strlen (string) + 1);
- sindex = 0;
-
- for (;;)
- {
- c = string[sindex];
- if (c == '\0')
- break;
-
- switch (c)
- {
- case '\\':
- c = string[++sindex];
- if (quoted && !member (c, slashify_in_quotes))
- {
- *r++ = '\\';
- *r++ = c;
- }
- else
- *r++ = c;
-
- sindex++;
- break;
-
- case '"':
- tindex = ++sindex;
- temp = string_extract_double_quoted (string, &tindex);
- sindex = tindex;
-
- if (string[sindex])
- sindex++;
-
- temp1 = string_quote_removal (temp, 1); /* XXX is this needed? */
-
- if (temp)
- free (temp);
-
- if (temp1)
- {
- strcpy (r, temp1);
- r += strlen (r);
- free (temp1);
- }
- break;
-
- case '\'':
- if (quoted)
- {
- *r++ = c;
- sindex++;
- }
- else
- {
- tindex = ++sindex;
- temp = string_extract_verbatim (string, &tindex, "'");
- sindex = tindex;
-
- if (string[sindex])
- sindex++;
-
- if (temp)
- {
- strcpy (r, temp);
- r += strlen (r);
- free (temp);
- }
- }
- break;
- default:
- *r++ = c;
- sindex++;
- break;
- }
- }
- *r = '\0';
- return (result_string);
- }
-
- /* Perform quote removal on word WORD. This allocates and returns a new
- WORD_DESC *. */
- WORD_DESC *
- word_quote_removal (word, quoted)
- WORD_DESC *word;
- int quoted;
- {
- WORD_DESC *w;
- char *t;
-
- t = string_quote_removal (word->word, quoted);
- w = make_word (t);
- return (w);
- }
-
- /* Perform quote removal on all words in LIST. If QUOTED is non-zero,
- the members of the list are treated as if they are surrounded by
- double quotes. Return a new list, or NULL if LIST is NULL. */
- WORD_LIST *
- word_list_quote_removal (list, quoted)
- WORD_LIST *list;
- int quoted;
- {
- WORD_LIST *result = (WORD_LIST *)NULL, *t, *tresult;
-
- t = list;
- while (t)
- {
- tresult = (WORD_LIST *)xmalloc (sizeof (WORD_LIST));
- tresult->word = word_quote_removal (t->word, quoted);
- tresult->next = (WORD_LIST *)NULL;
- result = (WORD_LIST *) list_append (result, tresult);
- t = t->next;
- }
- return (result);
- }
-
- #if defined (NOTDEF)
- /* Currently unused. */
- /* Return 1 if CHARACTER appears in an unquoted portion of
- STRING. Return 0 otherwise. */
- static int
- unquoted_member (character, string)
- int character;
- char *string;
- {
- int sindex, tindex, c;
- char *temp;
-
- sindex = 0;
-
- while (c = string[sindex])
- {
- if (c == character)
- return (1);
-
- switch (c)
- {
- case '\\':
- sindex++;
- if (string[sindex])
- sindex++;
- break;
-
- case '"':
- case '\'':
-
- tindex = ++sindex;
- if (c == '"')
- temp = string_extract_double_quoted (string, &tindex);
- else
- temp = string_extract_verbatim (string, &tindex, "'");
- sindex = tindex;
-
- if (string[sindex])
- sindex++;
-
- if (temp)
- free (temp);
- break;
-
- default:
- sindex++;
- break;
- }
- }
- return (0);
- }
- #endif /* NOTDEF */
-
- /* Return 1 if SUBSTR appears in an unquoted portion of STRING. */
- static int
- unquoted_substring (substr, string)
- char *substr, *string;
- {
- int sindex, tindex, c, sublen;
- char *temp;
-
- if (!substr || !*substr)
- return (0);
-
- sublen = strlen (substr);
- sindex = 0;
-
- while (c = string[sindex])
- {
- if (c == *substr &&
- strncmp (string + sindex, substr, sublen) == 0)
- return (1);
-
- switch (c)
- {
- case '\\':
- sindex++;
-
- if (string[sindex])
- sindex++;
- break;
-
- case '"':
- case '\'':
-
- tindex = ++sindex;
- if (c == '"')
- temp = string_extract_double_quoted (string, &tindex);
- else
- temp = string_extract_verbatim (string, &tindex, "'");
- sindex = tindex;
-
- if (string[sindex])
- sindex++;
-
- if (temp)
- free (temp);
-
- break;
-
- default:
- sindex++;
- break;
- }
- }
- return (0);
- }
-
- /*******************************************
- * *
- * Functions to perform word splitting *
- * *
- *******************************************/
-
- /* This splits a single word into a WORD LIST on $IFS, but only if the word
- is not quoted. list_string () performs quote removal for us, even if we
- don't do any splitting. */
- WORD_LIST *
- word_split (w)
- WORD_DESC *w;
- {
- WORD_LIST *result;
-
- if (w)
- {
- SHELL_VAR *ifs = find_variable ("IFS");
- char *ifs_chars;
-
- /* If IFS is unset, it defaults to " \t\n". */
- if (ifs)
- ifs_chars = value_cell (ifs);
- else
- ifs_chars = " \t\n";
-
- if (w->quoted || !ifs_chars)
- ifs_chars = "";
-
- result = list_string (w->word, ifs_chars, w->quoted);
- }
- else
- result = (WORD_LIST *)NULL;
- return (result);
- }
-
- /* Perform word splitting on LIST and return the RESULT. It is possible
- to return (WORD_LIST *)NULL. */
- WORD_LIST *
- word_list_split (list)
- WORD_LIST *list;
- {
- WORD_LIST *result = (WORD_LIST *)NULL, *t, *tresult;
-
- t = list;
- while (t)
- {
- tresult = word_split (t->word);
- result = (WORD_LIST *) list_append (result, tresult);
- t = t->next;
- }
- return (result);
- }
-
- /**************************************************
- * *
- * Functions to expand an entire WORD_LIST *
- * *
- **************************************************/
-
- /* Do all of the assignments in LIST up to a word which isn't an
- assignment. */
- WORD_LIST *
- get_rid_of_variable_assignments (list)
- WORD_LIST *list;
- {
- WORD_LIST *orig = list;
-
- while (list)
- if (!list->word->assignment)
- {
- WORD_LIST *new_list = copy_word_list (list);
- dispose_words (orig);
- return (new_list);
- }
- else
- {
- do_assignment (list->word->word);
- list = list->next;
- }
- dispose_words (orig);
- return ((WORD_LIST *)NULL);
- }
-
- /* Check and handle the case where there are some variable assignments
- in LIST which go into the environment for this command. */
- WORD_LIST *
- get_rid_of_environment_assignments (list)
- WORD_LIST *list;
- {
- register WORD_LIST *tlist = list;
- register WORD_LIST *new_list;
-
- while (tlist)
- {
- if (!tlist->word->assignment) goto make_assignments;
- tlist = tlist->next;
- }
- /* Since all of the assignments are variable assignments. */
- return (list);
-
- make_assignments:
- tlist = list;
- while (tlist)
- {
- if (tlist->word->assignment)
- assign_in_env (tlist->word->word);
- else
- {
- if (!place_keywords_in_env)
- {
- new_list = copy_word_list (tlist);
- dispose_words (list);
- return (new_list);
- }
- }
- tlist = tlist->next;
- }
-
- /* We got all of the keywords assigned. Now return the remainder
- of the words. */
- {
- register WORD_LIST *new_list = (WORD_LIST *)NULL;
-
- tlist = list;
-
- /* Skip the ones at the start. */
- while (tlist && tlist->word->assignment)
- tlist = tlist->next;
-
- /* If we placed all the keywords in the list into the environment,
- then remove them from the output list. */
- if (place_keywords_in_env)
- {
- while (tlist)
- {
- if (!tlist->word->assignment)
- new_list = make_word_list (copy_word (tlist->word), new_list);
- tlist = tlist->next;
- }
- new_list = (WORD_LIST *)reverse_list (new_list);
- }
- else
- {
- /* Just copy the list. */
- new_list = copy_word_list (tlist);
- }
- dispose_words (list);
- return (new_list);
- }
- }
-
- /* Take the list of words in LIST and do the various substitutions. Return
- a new list of words which is the expanded list, and without things like
- variable assignments. */
- static WORD_LIST *expand_words_internal ();
-
- WORD_LIST *
- expand_words (list)
- WORD_LIST *list;
- {
- return (expand_words_internal (list, 1));
- }
-
- /* Same as expand_words (), but doesn't hack variable or environment
- variables. */
- WORD_LIST *
- expand_words_no_vars (list)
- WORD_LIST *list;
- {
- return (expand_words_internal (list, 0));
- }
-
- /* Non-zero means to allow unmatched globbed filenames to expand to
- a null file. */
- static int allow_null_glob_expansion = 0;
-
- /* The workhorse for expand_words () and expand_words_no_var ().
- First arg is LIST, a WORD_LIST of words.
- Second arg DO_VARS is non-zero if you want to do environment and
- variable assignments, else zero.
-
- This does all of the subsitutions: brace expansion, tilde expansion,
- parameter expansion, command substitution, arithmetic expansion,
- word splitting, and pathname expansion. */
- static WORD_LIST *
- expand_words_internal (list, do_vars)
- WORD_LIST *list;
- int do_vars;
- {
- register WORD_LIST *tlist, *new_list = (WORD_LIST *)NULL;
- WORD_LIST *orig_list;
- extern int no_brace_expansion;
-
- tlist = (WORD_LIST *)copy_word_list (list);
-
- if (do_vars)
- {
- /* Handle the case where the arguments are assignments for
- the environment of this command. */
- tlist = get_rid_of_environment_assignments (tlist);
-
- /* Handle the case where the arguments are all variable assignments. */
- tlist = get_rid_of_variable_assignments (tlist);
- }
-
- /* Begin expanding the words that remain. The expansions take place on
- things that aren't really variable assignments. */
-
- if (!tlist)
- return ((WORD_LIST *)NULL);
-
- /* Do brace expansion on this word if there are any brace characters
- in the string. */
- if (!no_brace_expansion)
- {
- extern char **brace_expand ();
- register char **expansions;
- WORD_LIST *braces = (WORD_LIST *)NULL;
- int eindex;
-
- orig_list = tlist;
-
- while (tlist)
- {
- /* Only do brace expansion if the word has a brace character. If
- not, just copy the word list element, add it to braces, and
- continue. In the common case, at least when running shell
- scripts, this will degenerate to a bunch of calls to `index',
- and then what is basically the body of copy_word_list. */
- if (index (tlist->word->word, '{') != NULL)
- {
- expansions = brace_expand (tlist->word->word);
-
- for (eindex = 0; expansions[eindex]; eindex++)
- {
- braces = make_word_list (make_word (expansions[eindex]),
- braces);
- free (expansions[eindex]);
- }
- free (expansions);
- }
- else
- {
- WORD_LIST *new = (WORD_LIST *)xmalloc (sizeof (WORD_LIST));
- new->word = copy_word (tlist->word);
- new->next = braces;
- braces = new;
- }
-
- tlist = tlist->next;
- }
- dispose_words (orig_list);
- tlist = (WORD_LIST *)reverse_list (braces);
- }
-
- orig_list = tlist;
-
- /* We do tilde expansion all the time. This is what 1003.2 says. */
- while (tlist)
- {
- register char *current_word;
- WORD_LIST *expanded, *t;
- int expanded_something = 0;
-
- current_word = tlist->word->word;
-
- if (current_word[0] == '~' ||
- (index (current_word, '~') &&
- unquoted_substring ("=~", current_word)))
- {
- char *tilde_expand (), *tt;
-
- tt = tlist->word->word;
- tlist->word->word = tilde_expand (tt);
- free (tt);
- }
-
- expanded = expand_word_internal
- (tlist->word, 0, (int *)NULL, &expanded_something);
-
- if (expanded_something)
- t = word_list_split (expanded);
- else
- {
- /* If no parameter expansion, command substitution, or arithmetic
- substitution took place, then do not do word splitting. We
- still have to remove quoted null characters from the result. */
- word_list_remove_quoted_nulls (expanded);
- t = copy_word_list (expanded);
- }
-
- new_list =
- (WORD_LIST *)list_append (reverse_list (t), new_list);
-
- dispose_words (expanded);
-
- tlist = tlist->next;
- }
-
- new_list = (WORD_LIST *)reverse_list (new_list);
-
- dispose_words (orig_list);
-
- /* Okay, we're almost done. Now let's just do some filename
- globbing. */
- {
- char **shell_glob_filename (), **temp_list = (char **)NULL;
- register int list_index;
- WORD_LIST *glob_list;
-
- orig_list = (WORD_LIST *)NULL;
- tlist = new_list;
-
- if (!disallow_filename_globbing)
- {
- while (tlist)
- {
- /* If the word isn't quoted, then glob it. */
- if (!tlist->word->quoted && glob_pattern_p (tlist->word->word, 0))
- {
- temp_list = shell_glob_filename (tlist->word->word);
-
- /* Fix the hi-bits. (This is how we quoted
- special characters.) */
- {
- register char *t = dequote_string (tlist->word->word);
- free (tlist->word->word);
- tlist->word->word = t;
- }
-
- /* Handle error cases.
- I don't think we should report errors like "No such file
- or directory". However, I would like to report errors
- like "Read failed". */
-
- #if defined (USE_GLOB_LIBRARY)
- if (!temp_list)
- #else
- if (temp_list == (char **)-1)
- #endif /* !USE_GLOB_LIBRARY */
- {
- /* file_error (tlist->word->word); */
- /* A small memory leak, I think */
- temp_list = (char **) xmalloc (sizeof (char *));
- temp_list[0] = '\0';
- }
-
- #if !defined (USE_GLOB_LIBRARY)
- if (!temp_list)
- abort ();
- #endif /* !USE_GLOB_LIBRARY */
-
- /* Make the array into a word list. */
- glob_list = (WORD_LIST *)NULL;
- for (list_index = 0; temp_list[list_index]; list_index++)
- glob_list =
- make_word_list (make_word (temp_list[list_index]), glob_list);
-
- if (glob_list)
- orig_list = (WORD_LIST *)list_append (glob_list, orig_list);
- else
- if (!allow_null_glob_expansion)
- orig_list =
- make_word_list (copy_word (tlist->word), orig_list);
- }
- else
- {
- /* Fix the hi-bits. (This is how we quoted special
- characters.) */
- register char *t = dequote_string (tlist->word->word);
- free (tlist->word->word);
- tlist->word->word = t;
- orig_list = make_word_list (copy_word (tlist->word), orig_list);
- }
-
- free_array (temp_list);
- temp_list = (char **)NULL;
-
- tlist = tlist->next;
- }
- dispose_words (new_list);
- new_list = orig_list;
- }
- else
- {
- /* Fix the hi-bits. (This is how we quoted special characters.) */
- register WORD_LIST *wl = new_list;
- register char *wp;
- while (wl)
- {
- wp = dequote_string (wl->word->word);
- free (wl->word->word);
- wl->word->word = wp;
- wl = wl->next;
- }
- return (new_list);
- }
- }
- return (WORD_LIST *)(reverse_list (new_list));
- }
-
- /* Call the glob library to do globbing on PATHNAME.
- PATHNAME can contain characters with the hi bit set; this indicates
- that the character is to be quoted. We quote it here. */
- char **
- shell_glob_filename (pathname)
- char *pathname;
- #if defined (USE_GLOB_LIBRARY)
- {
- extern int glob_dot_filenames;
- register int i, j;
- char *temp, **return_value;
- glob_t filenames;
- int glob_flags;
-
- temp = (char *)alloca (1 + (2 * strlen (pathname)));
-
- for (i = j = 0; pathname[i]; i++, j++)
- {
- if (QUOTED_CHAR (pathname[i]))
- temp[j++] = '\\';
-
- temp[j] = DEQUOTE_CHAR (pathname[i]);
- }
- temp[j] = '\0';
-
- filenames.gl_offs = 0;
-
- glob_flags = glob_dot_filenames ? GLOB_PERIOD : 0;
- glob_flags |= (GLOB_ERR | GLOB_DOOFFS);
-
- i = glob (temp, glob_flags, (Function *)NULL, &filenames);
-
- if (i == GLOB_NOSPACE || i == GLOB_ABEND)
- return ((char **)NULL);
-
- if (i == GLOB_NOMATCH)
- filenames.gl_pathv[0] = (char *)NULL;
-
- return (filenames.gl_pathv);
- }
- #else /* !USE_GLOB_LIBRARY */
- {
- extern char **glob_filename ();
- extern int glob_dot_filenames, noglob_dot_filenames;
- register int i, j;
- char *temp, **results;
-
- temp = (char *)alloca (1 + (2 * strlen (pathname)));
-
- noglob_dot_filenames = !glob_dot_filenames;
-
- for (i = j = 0; pathname[i]; i++, j++)
- {
- if (QUOTED_CHAR (pathname[i]))
- {
- temp[j++] = '\\';
- temp[j] = DEQUOTE_CHAR (pathname[i]);
- }
- else
- temp[j] = pathname[i];
- }
- temp[j] = '\0';
-
- results = glob_filename (temp);
-
- if (results && results != (char **)-1)
- sort_char_array (results);
-
- return (results);
- }
- #endif /* !USE_GLOB_LIBRARY */
-
- /*************************************************
- * *
- * Functions to manage special variables *
- * *
- *************************************************/
-
- /* An alist of name.function for each special variable. Most of the
- functions don't do much, and in fact, this would be faster with a
- switch statement, but by the end of this file, I am sick of switch
- statements. */
-
- /* The functions that get called. */
- int
- sv_path (), sv_mail (), sv_terminal (), sv_histsize (), sv_histfilesize (),
- sv_uids (), sv_ignoreeof (), sv_glob_dot_filenames (), sv_histchars (),
- sv_nolinks (), sv_hostname_completion_file (), sv_history_control (),
- sv_noclobber (), sv_allow_null_glob_expansion (),
- sv_command_oriented_history ();
-
- #if defined (GETOPTS_BUILTIN)
- int sv_optind (), sv_opterr ();
- #endif /* GETOPTS_BUILTIN */
-
- #if defined (JOB_CONTROL)
- extern int sv_notify ();
- #endif
-
- struct name_and_function {
- char *name;
- Function *function;
- } special_vars[] = {
- { "PATH", sv_path },
- { "MAIL", sv_mail },
- { "MAILPATH", sv_mail },
- { "MAILCHECK", sv_mail },
- { "TERMCAP", sv_terminal },
- { "TERM", sv_terminal },
- { "HISTSIZE", sv_histsize },
- { "HISTFILESIZE", sv_histfilesize },
- { "EUID", sv_uids},
- { "UID", sv_uids},
- { "IGNOREEOF", sv_ignoreeof },
- { "ignoreeof", sv_ignoreeof },
- #if defined (GETOPTS_BUILTIN)
- { "OPTIND", sv_optind },
- { "OPTERR", sv_opterr },
- #endif /* GETOPTS_BUILTIN */
- #if defined (JOB_CONTROL)
- { "notify", sv_notify },
- #endif /* JOB_CONTROL */
- { "glob_dot_filenames", sv_glob_dot_filenames },
- { "allow_null_glob_expansion", sv_allow_null_glob_expansion },
- { "command_oriented_history", sv_command_oriented_history },
- { "histchars", sv_histchars },
- { "hostname_completion_file", sv_hostname_completion_file },
- { "history_control", sv_history_control },
- { "noclobber", sv_noclobber },
- { "nolinks", sv_nolinks },
- { (char *)0x00, (Function *)0x00 }
- };
-
- /* The variable in NAME has just had its state changed. Check to see if it
- is one of the special ones where something special happens. */
- stupidly_hack_special_variables (name)
- char *name;
- {
- int i = 0;
-
- while (special_vars[i].name)
- {
- if (STREQ (special_vars[i].name, name))
- {
- (*(special_vars[i].function)) (name);
- return;
- }
- i++;
- }
- }
-
- /* Set/unset noclobber. */
- sv_noclobber (name)
- char *name;
- {
- extern int noclobber;
-
- if (find_variable (name))
- noclobber = 1;
- else
- noclobber = 0;
- }
-
- /* What to do just after the PATH variable has changed. */
- sv_path (name)
- char *name;
- {
- /* hash -r */
- WORD_LIST *args;
-
- args = make_word_list (make_word ("-r"), NULL);
- hash_builtin (args);
- dispose_words (args);
- }
-
- /* What to do just after one of the MAILxxxx variables has changed. NAME
- is the name of the variable. */
- sv_mail (name)
- char *name;
- {
- /* If the time interval for checking the files has changed, then
- reset the mail timer. Otherwise, one of the pathname vars
- to the users mailbox has changed, so rebuild the array of
- filenames. */
- if (strcmp (name, "MAILCHECK") == 0)
- reset_mail_timer ();
- else
- {
- if ((strcmp (name, "MAIL") == 0) || (strcmp (name, "MAILPATH") == 0))
- {
- free_mail_files ();
- remember_mail_dates ();
- }
- }
- }
-
- /* What to do just after one of the TERMxxx variables has changed.
- If we are an interactive shell, then try to reset the terminal
- information in readline. */
- sv_terminal (name)
- char *name;
- {
- extern int interactive;
-
- if (interactive)
- rl_reset_terminal (get_string_value ("TERM"));
- }
-
- /* What to do after the HISTSIZE variable changes.
- If there is a value for this variable (and it is numeric), then stifle
- the history. Otherwise, if there is NO value for this variable,
- unstifle the history. */
- sv_histsize (name)
- char *name;
- {
- char *temp = get_string_value (name);
-
- if (temp)
- {
- int num;
- if (sscanf (temp, "%d", &num) == 1)
- {
- extern int history_lines_this_session;
-
- stifle_history (num);
- if (history_lines_this_session > where_history ())
- history_lines_this_session = where_history ();
- }
- }
- else
- unstifle_history ();
- }
-
- /* What to do if the HISTFILESIZE variable changes. */
- sv_histfilesize (name)
- char *name;
- {
- char *temp = get_string_value (name);
-
- if (temp)
- {
- extern int history_lines_in_file;
- int num;
- if (sscanf (temp, "%d", &num) == 1)
- {
- history_truncate_file (get_string_value ("HISTFILE"), num);
- if (num <= history_lines_in_file)
- history_lines_in_file = num;
- }
- }
- }
-
- /* A nit for picking at history saving.
- Value of 0 means save all lines parsed by the shell on the history.
- Value of 1 means save all lines that do not start with a space.
- Value of 2 means save all lines that do not match the last line saved. */
- int history_control = 0;
-
- /* What to do after the HISTORY_CONTROL variable changes. */
- sv_history_control (name)
- char *name;
- {
- char *temp = get_string_value (name);
-
- history_control = 0;
-
- if (temp && *temp)
- {
- if (strcmp (temp, "ignorespace") == 0)
- history_control = 1;
- else if (strcmp (temp, "ignoredups") == 0)
- history_control = 2;
- }
- }
-
- /* By default, every line is saved in the history individually. I.e.,
- if the user enters:
- bash$ for i in a b c
- > do
- > echo $i
- > done
- Each line will be individually saved in the history.
- bash$ history
- 10 for i in a b c
- 11 do
- 12 echo $i
- 13 done
- 14 history
- If the variable command_oriented_history is set, multiple lines
- which form one command will be saved as one history entry.
- bash$ for i in a b c
- > do
- > echo $i
- > done
- bash$ history
- 10 for i in a b c
- do
- echo $i
- done
- 11 history
- The user can then recall the whole command all at once instead
- of just being able to recall one line at a time.
- */
- int command_oriented_history = 0;
-
- /* What to do after the COMMAND_ORIENTED_HISTORY variable changes. */
- sv_command_oriented_history (name)
- char *name;
- {
- if (find_variable (name) != (SHELL_VAR *)NULL)
- command_oriented_history = 1;
- else
- command_oriented_history = 0;
- }
-
- /* If the variable exists, then the value of it can be the number
- of times we actually ignore the EOF. The default is small,
- (smaller than csh, anyway). */
- sv_ignoreeof (name)
- char *name;
- {
- extern int eof_encountered, eof_encountered_limit;
- char *temp = get_string_value (name);
- int new_limit;
-
- eof_encountered = 0;
-
- if (temp && (sscanf (temp, "%d", &new_limit) == 1))
- eof_encountered_limit = new_limit;
- else
- eof_encountered_limit = 10; /* csh uses 26. */
- }
-
- /* Control whether * matches .files in globbing. Yechh. */
- int glob_dot_filenames = 0;
-
- sv_glob_dot_filenames (name)
- char *name;
- {
- if (find_variable (name) != (SHELL_VAR *)NULL)
- glob_dot_filenames = 1;
- else
- glob_dot_filenames = 0;
- }
-
- /* Setting/unsetting of the history expansion character. */
- char old_history_expansion_char = '!';
- char old_history_comment_char = '#';
- char old_history_subst_char = '^';
-
- sv_histchars (name)
- char *name;
- {
- extern char history_expansion_char;
- extern char history_comment_char;
- extern char history_subst_char;
- char *temp = get_string_value (name);
-
- if (temp)
- {
- old_history_expansion_char = history_expansion_char;
- history_expansion_char = *temp;
-
- if (temp[1])
- {
- old_history_subst_char = history_subst_char;
- history_subst_char = temp[1];
-
- if (temp[2])
- {
- old_history_comment_char = history_comment_char;
- history_comment_char = temp[2];
- }
- }
- }
- else
- {
- history_expansion_char = '!';
- history_subst_char = '^';
- history_comment_char = '#';
- }
- }
-
- #if defined (JOB_CONTROL)
- /* Job notification feature desired? */
- sv_notify (name)
- char *name;
- {
- extern int asynchronous_notification;
-
- if (get_string_value (name))
- asynchronous_notification = 1;
- else
- asynchronous_notification = 0;
- }
- #endif /* JOB_CONTROL */
-
- /* If the variable `nolinks' exists, it specifies that symbolic links are
- not to be followed in `cd' commands. */
- sv_nolinks (name)
- char *name;
- {
- extern int follow_symbolic_links;
-
- follow_symbolic_links = !find_variable (name);
- }
-
- /* Don't let users hack the user id variables. */
- sv_uids (name)
- char *name;
- {
- int uid = getuid ();
- int euid = geteuid ();
- char buff[10];
- register SHELL_VAR *v;
-
- sprintf (buff, "%d", uid);
- v = find_variable ("UID");
- if (v)
- v->attributes &= ~att_readonly;
-
- v = bind_variable ("UID", buff);
- v->attributes |= (att_readonly | att_integer);
-
- sprintf (buff, "%d", euid);
- v = find_variable ("EUID");
- if (v)
- v->attributes &= ~att_readonly;
-
- v = bind_variable ("EUID", buff);
- v->attributes |= (att_readonly | att_integer);
- }
-
- sv_hostname_completion_file (name)
- char *name;
- {
- extern int hostname_list_initialized;
-
- hostname_list_initialized = 0;
- }
-
- sv_allow_null_glob_expansion (name)
- char *name;
- {
- allow_null_glob_expansion = (int)find_variable (name);
- }
-
- #if defined (GETOPTS_BUILTIN)
- sv_optind (name)
- char *name;
- {
- char *tt = get_string_value ("OPTIND");
- int s = 0;
-
- if (tt && *tt)
- {
- s = atoi (tt);
-
- /* According to POSIX, setting OPTIND=1 resets the internal state
- of getopt (). */
- if (s < 0 || s == 1)
- s = 0;
- }
- getopts_reset (s);
- }
-
- int
- sv_opterr (name)
- char *name;
- {
- char *tt = get_string_value ("OPTERR");
- int s = 1;
- extern int opterr;
-
- if (tt)
- s = atoi (tt);
- opterr = s;
- return (0);
- }
- #endif /* GETOPTS_BUILTIN */
-